#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cassert>
#include <string>

using namespace std;
int tickets = 10000;
#define THREADNUM 5
//多线程访问同一个全局变量，并对它进行数据计算，多线程会互相影响吗？
// 加锁保护：加锁的时候，一定要保证加锁的粒度，越小越好
// pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;//pthread_t就是pthread线程库里定义的一个数据类型
//加锁了之后，线程在临界区会是否会切换，会有问题吗？
//会切换吗？不会！第一次理解：虽然被切换了，但是你是持有锁被切换了，
//其他继续想访问临界区代码，必须先申请锁，但无法申请成功，所有其他线程无法访问临界区代码，就保持了临界区中的数据一致性
//如果我是一个线程，我不申请锁，我想访问临界区。--这是错误的编码
//在没有持有锁的线程看来，只有两种情况有意义，1.线程1没有持有锁(什么都没做) 2.线程1释放锁(做完了)，此时我可以申请锁了！
//加锁就是串行执行了吗？是的，临界区代码一定是串行执行的
//要访问临界资源，每一个线程都必须看到&& 访问锁，锁也是一种共享资源
//谁来保证锁的安全？？所有，申请和释放锁，必须是原子的！！！
//如何保证？锁是如何实现了?
//在cpu中，有寄存器存放线程的临时数据，也就是进程的上下文，在申请锁时，cpu把内存中的锁值1交换到寄存器中，这是寄存器中是1，内存中变为0
//这时进程被切换，其他进程想要申请锁，则必须要让cpu寄存器的值为1，但上一个线程把寄存器中的1也就是上一个线程上下文带走了，这时寄存器为原来的值0，也就只能阻塞

class ThreadData//创建一个存放进程数据的类，可通过创建进程将第四个参数设为ThreadData的指针
{
public:
    ThreadData(const string &n, pthread_mutex_t *pm) : tname(n.c_str()), pmtx(pm)
    {
    }

public:
    string tname;//打印值
    pthread_mutex_t *pmtx;//锁的地址
};
void *getTickets(void *args)
{
    ThreadData *td = (ThreadData *)args;//将传过来的void*地址转化为ThreadData*
    while (true)
    {
        int n = pthread_mutex_lock(td->pmtx);//申请锁
        assert(n == 0);
        if (tickets > 0)//临界区
        {
            usleep(1000);
            tickets--;
            n = pthread_mutex_unlock(td->pmtx);//解锁
            assert(n == 0);
            printf("%s,%d\n", td->tname.c_str(), tickets);
        }
        else
        {
            n = pthread_mutex_unlock(td->pmtx);//解锁
            assert(n == 0);
            break;
        }
    }
    delete td;//申请的空间释放
    return nullptr;
}
int main()
{
    pthread_mutex_t mtx;//定义锁
    pthread_mutex_init(&mtx, nullptr);//锁的初始化
    pthread_t tid[THREADNUM];
    for (int i = 0; i < THREADNUM; i++)
    {
        string name("new thread");
        name += to_string(i + 1);
        ThreadData *td = new ThreadData(name, &mtx);//申请threadData
        pthread_create(tid + i, nullptr, getTickets, (void *)td);//将对象的指针当参数传过去
    }

    for (int i = 0; i < THREADNUM; i++)
    {
        pthread_join(tid[i], nullptr);
    }

    pthread_mutex_destroy(&mtx);//锁的销毁
    return 0;
}